package com.cfpamf.ms.insur.wx.util;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.ByteOrderMark;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils;

import java.io.BufferedReader;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;
import java.util.function.Predicate;

/**
 * Create By zhengjing on 2019-10-29 14:40
 */
@Slf4j
public final class CsvReaderUtil {

    private static final DateTimeFormatter TIME_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    private static final String CSV_SEPARATOR = ",";

    static ConcurrentHashMap<String, Field[]> fieldCache = new ConcurrentHashMap<>();

    /**
     * 默认的过滤器
     */
    static Predicate<Field> DEFAULT_FILTER = field -> true;

    public static <T> T parseCsv(String content, Class<T> clazz) {

        return parseCsv(content, clazz, DEFAULT_FILTER);
    }

    /**
     * 分页读取数据 5000行处理一次
     *
     * @param br
     * @param consumer
     */
    public static void pageReader(BufferedReader br, BiConsumer<List<String>, Boolean> consumer) {

        List<String> data = new ArrayList<>(5000);
        try {
//            br.mark(0);
//            int read = br.read();
//            br.reset();
//            if (read == ByteOrderMark.UTF_BOM) {
//                long skip = br.skip(0);
//                System.err.println(skip);
//            }
            //去除Unicode BOM character
            String s = br.readLine();
            if (s.charAt(0) == ByteOrderMark.UTF_BOM) {
                s = s.substring(1);
                data.add(s);
            }
            while (true) {
                s = br.readLine();
                if (s == null) {
                    break;
                }
                data.add(s);
                if (data.size() >= 5000) {
                    //再读取一行 判断当前是否已经结束
                    String s1 = br.readLine();
                    Boolean isEnd = Objects.isNull(s1) ? Boolean.TRUE : Boolean.FALSE;
                    consumer.accept(data, isEnd);
                    data.clear();
                    //将刚刚读取的数据放到下一次处理的集合中
                    data.add(s1);

                }
            }
        } catch (IOException e) {
            throw new IllegalStateException("读取文件失败", e);
        }
        if (!data.isEmpty()) {
            consumer.accept(data, Boolean.TRUE);
        }
    }

    public static <T> T parseCsv(String content, Class<T> clazz, Predicate<Field> filter) {

        if (StringUtils.isBlank(content)) return null;

        //获取字段
        String clazzName = clazz.getName();
        Field[] fields = fieldCache.get(clazz.getName());
        if (fields == null) {
            fields = init(clazz);
            fieldCache.put(clazzName, fields);
        }
        //过滤数据
        if (filter != null) {
            fields = Arrays.stream(fields)
                    .filter(filter).toArray(Field[]::new);
        }
        // 将这个前缀去掉
//        content = content.replaceAll("`", "");
        String[] values = content.split(CSV_SEPARATOR);
        if (values.length > fields.length) {
            throw new IllegalArgumentException("解析CSV数据失败,数据对象字段大于定义对象字段:" + content);
        }
        T result;
        try {
            result = clazz.newInstance();
        } catch (Exception e) {
            throw new IllegalArgumentException(clazzName + ".newInstance()失败 请检查是否存在public " + clazzName + "()");
        }
        for (int i = 0; i < values.length; i++) {
            Field declaredField = fields[i];
            try {
                Class<?> type = declaredField.getType();
                String valStr = values[i];
                Object val = "";
                if (StringUtils.isNotBlank(valStr)) {
                    valStr = valStr.trim();
                    val = valStr;
                    if (type == BigDecimal.class) {
                        val = new BigDecimal(valStr);
                    } else if (type == LocalDateTime.class) {
                        val = LocalDateTime.parse(valStr, TIME_FMT);
                    } else if (type == Integer.class) {
                        val = Integer.parseInt(valStr);
                    }
                }
                FieldUtils.writeField(declaredField, result, val, true);
            } catch (IllegalAccessException e) {
                //skip not happend
                log.error("解析csv数据失败，赋值失败", e);
            }
        }
        return result;

    }

    private static Field[] init(Class<?> clazz) {
        return Arrays.stream(clazz.getDeclaredFields())
                .filter(field -> !Modifier.isStatic(field.getModifiers()))
                .toArray(Field[]::new);
    }

}
